package com.zxwl.base.dao;

import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.dialect.Dialect;
import org.hibernate.impl.SessionFactoryImpl;
import org.hibernate.property.Getter;
import org.hibernate.property.Setter;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.SessionFactoryUtils;

import com.zxwl.common.util.GenericsUtils;


public abstract class BaseDao<T> implements IBaseDao<T>
{

	
	@SuppressWarnings("unchecked")
	protected Class<T> entityClass = GenericsUtils.getSuperClassGenricType(this.getClass());
	protected EntityHelper<T> entityHelper;
	protected Getter timestampGetter;
	protected Setter timestampSetter;
	protected Getter idGetter;
	protected HibernateTemplate hibernateTemplate;

	
	@SuppressWarnings("hiding")
	public <T>BaseDao()
	{
		this._getEntityHelper();
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List executeProcedure(final String procedureName, final Object[] procParams)
	{
		return this.getHibernateTemplate().execute(new HibernateCallback()
		{
			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException
			{
				Connection con = SessionFactoryUtils.getDataSource(session.getSessionFactory()).getConnection();
				String queryString = "";
				String queryParams = "";
				queryString = "{call " + procedureName;
				if (procParams != null && procParams.length > 0)
				{
					for (int i = 0; i < procParams.length - 1 ; i++)
					{
						queryParams += "?,";
					}
					queryString = queryString + "(" + queryParams + "?)}";
				}
				else
				{
					queryString = queryString + "(" + queryParams + "?)}";
				}
				CallableStatement cs = con.prepareCall(queryString);
				int i = 1;
				if (procParams != null && procParams.length > 0)
				{
					for (Object param : procParams)
					{
						cs.setString(i, param.toString());
						i++;
					}
				}
				ResultSet rs = null;
				try
				{
					rs = cs.executeQuery();
					// 读取游标参数
					int colCount = rs.getMetaData().getColumnCount();
					String colNameList[] = new String[colCount];
					for (i = 0; i < colCount; i++)
					{
						colNameList[i] = rs.getMetaData().getColumnName(i + 1);
					}
					List resultList = new ArrayList();

					while (rs.next())
					{
						Map rowMap = new HashMap();
						for (i = 0; i < colCount; i++)
						{
							Object val = rs.getObject(i + 1);
							if (null != val)
							{
								if (val instanceof java.sql.Date)
								{
									val = rs.getTimestamp(i + 1);
									rowMap.put(colNameList[i], val.toString());
								}
								else
								{
									rowMap.put(colNameList[i], val.toString());
								}
							}
							else
							{
								rowMap.put(colNameList[i], null);
							}

						}
						resultList.add(rowMap);
					}

					rs.close();
					cs.close();
					con.close();
					return resultList;
			}
			catch (SQLException e)
			{
				if(null != rs)
				{
					rs.close();
				}
				if(null != cs)
				{
					cs.close();
				}
				if(null != con)
				{
					con.close();
				}
				//e.printStackTrace();
				System.out.println(e.getMessage());
			}
		
			return null;
		}
		});
	}

	public String getTimestamp()
	{
		return new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
	}
	
	protected EntityHelper<T> _getEntityHelper()
	{
		try
		{
			if(this.entityHelper == null)
			{
				this.entityHelper = new EntityHelper<T>(entityClass);
				
				if(this.entityHelper != null)
				{
					PropertyInfo timestampProp = this.entityHelper.getProperty("TIMESTAMP");
					if(timestampProp != null)
					{
						timestampGetter = timestampProp.getGetter();
						timestampSetter = timestampProp.getSetter();
					}
					else
					{
					}
					PropertyInfo idProp = this.entityHelper.getProperty("ID");
					if(idProp != null)
					{
						idGetter =  idProp.getGetter();
					}
					else
					{
						
					}
				}
			}
		}
		catch(Exception e)
		{
		}
		return this.entityHelper;
	}
	
	public T createEntity() throws InstantiationException, IllegalAccessException
	{
		return entityClass.newInstance();
	}
	
	public HibernateTemplate getHibernateTemplate()
	{
		return hibernateTemplate;
	}

	@Resource
	public void setHibernateTemplate(HibernateTemplate hibernateTemplate)
	{
		this.hibernateTemplate = hibernateTemplate;
	}

	
	public IProcedure<T> getProcedure()
	{
		IProcedure<T> procedure = null;
		SessionFactoryImpl sessionFactory = (SessionFactoryImpl)this.getHibernateTemplate().getSessionFactory();
		Dialect dialect = sessionFactory.getDialect();
		String dialectType = dialect.toString().toLowerCase();
		if(dialectType.indexOf("oracle")>0)
		{
			procedure = new ProcedureOracle<T>(this._getEntityHelper());
		}
		else if(dialectType.indexOf("sqlserver")>0)
		{
			procedure = new ProcedureSqlServer<T>(this._getEntityHelper());
		}
		else if(dialectType.indexOf("mysql")>0)
		{
			procedure = new ProcedureMySql<T>(this._getEntityHelper());
		}
		procedure.setHibernateTemplate(this.getHibernateTemplate());
		return procedure;
	}
	@SuppressWarnings("unchecked")
	protected List<T> _queryList(final String hql, final QueryParameter queryparameter)
	{
		return (List<T>) this.getHibernateTemplate().execute(new HibernateCallback<Object>()
		{
			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException
			{
				Query query = session.createQuery(hql);
				_setQueryParams(query, queryparameter);
				return query.list();
			}
		});
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> queryAll()
	{
		return (List<T>) this.getHibernateTemplate().execute(new HibernateCallback<Object>()
		{

			@Override
			public List<T> doInHibernate(Session session) throws HibernateException, SQLException
			{
				return session.createQuery(" from " + entityClass.getSimpleName()).list();
			}

		});
	}

	@Override
	public T queryById(Serializable entityId)
	{
		return (T) this.getHibernateTemplate().get(this.entityClass, entityId);
	}

	@Override
	public void save(T entity)
	{
		this.updateTimestamp(entity);
		this.getHibernateTemplate().save(entity);
	}

	@Override
	public void delete(Serializable... entityIds)
	{
		for (Serializable serId : entityIds)
		{
			this.getHibernateTemplate().delete(this.getHibernateTemplate().load(entityClass, serId));
		}
	}

	@Override
	public void delete(Serializable entityId)
	{
		this.getHibernateTemplate().delete(this.getHibernateTemplate().load(entityClass, entityId));
	}
	
	@Override
	public void update(T entity)
	{
		// 更新时间戳
		this.updateTimestamp(entity);
		this.getHibernateTemplate().update(entity);
	}

	protected T _query(final String hql, final QueryParameter queryParameter)
	{
		return (T) this.getHibernateTemplate().execute(new HibernateCallback<T>()
		{

			@SuppressWarnings("unchecked")
			@Override
			public T doInHibernate(Session session) throws HibernateException, SQLException
			{
				Query query = session.createQuery(hql);
				_setQueryParams(query, queryParameter);
				return (T) query.uniqueResult();
			}
		});
	}
	
	
	/**
	 * 执行sql语句更新
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public Integer executeUpdate(final String hql)
	{
		return this.getHibernateTemplate().execute(new HibernateCallback()
		{
			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException
			{
				Integer query = session.createQuery(hql).executeUpdate();
				return query;
			}
		});
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected long _queryRowCount(final String hql,final QueryParameter queryParams) 
	{
		String lowerhql = hql.toLowerCase();
		final String hqlofCount;
		String hqltemp;
		// 如果有select
		if ("select".equals(lowerhql.substring(0, 5)))
		{
			// 去掉from前的内容
			int fromPos = lowerhql.indexOf("from");

			hqltemp = "select count(*) " + hql.substring(fromPos);
		}
		else
		{
			hqltemp = "select count(*)  " + hql;
		}
		
		lowerhql = hqltemp.toLowerCase();
		// 去掉order by 子句
		int index = lowerhql.indexOf(" order ");
		if(index > 0)
		{
			hqltemp = hqltemp.substring(0,index);
		}
		hqlofCount = hqltemp;
		return this.getHibernateTemplate().execute(new HibernateCallback()
		{
			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException
			{
				Query query = session.createQuery(hqlofCount);
				_setQueryParams(query, queryParams);
				Object o = query.uniqueResult();
				Long result = (Long) o;// (Long) query.uniqueResult();
				Hibernate.initialize(result);
				return result;
			}
		});
	}

	protected void _setQueryParams(Query query,QueryParameter queryParameter)
	{
		if(queryParameter == null)
		{
			return ;
		}
		Set<String> names = queryParameter.findParameterNames();
		names.remove("pageSize");
		names.remove("pageNum");
		for(String name:names)
		{
			Parameter parameter = queryParameter.findParameter(name);
			if(parameter != null && queryParameter.containParameter(name))
			{
				Object value = parameter.findValue();
				if( value instanceof  List)
				{
					query.setParameterList(name, (Collection<?>)value);
				}
				else
				{
					query.setParameter(name, value);
				}
			}
		}
	}
	protected static void setQueryParams(Query query, Object[] queryParams)
	{
		if(queryParams != null)
		{
			int i = 0;
			for(Object param:queryParams)
			{
				query.setParameter(i, param);
				i++;
			}
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected List<T> _queryForPage(final String hql, final QueryParameter queryParams, final Integer pageNum,final Integer pageSize) 
	{
		return this.getHibernateTemplate().executeFind(new HibernateCallback()
		{

			@Override
			public Object doInHibernate(Session session) throws HibernateException, SQLException
			{
				Query query = session.createQuery(hql);
				_setQueryParams(query, queryParams);
				query.setFirstResult((pageNum - 1) * pageSize);
				query.setMaxResults(pageSize);
				return query.list();
			}
		});
	}
	
	@Override
	public List<Map<String, String>> queryMapListByProcedure(String procedureName, QueryParameter queryParameter)
	{
		return this.getProcedure().queryMapList(procedureName, queryParameter);  
	}
	
	public void queryForPageByProcedure(final String procedureName,DataPage<T> dataPage)
	{
		Integer beginIndex = (dataPage.getPageNum() - 1) * dataPage.getPageSize()+1;
		Integer endIndex = beginIndex + dataPage.getPageSize()-1;
		Integer rowSum = 0;
		
		QueryParameter queryParameter = dataPage.getQueryParameter();
		if(queryParameter == null)
		{
			queryParameter = new QueryParameter();
			dataPage.setQueryParameter(queryParameter);
		}
		
		queryParameter.addParameter("beginIndex", beginIndex);
		queryParameter.addParameter("endIndex", endIndex);
		queryParameter.addOutParameter("rowSum",rowSum);
		
		List<T> list = queryListByProcedure(procedureName,queryParameter);
		dataPage.setDataList(list);
		dataPage.setRowSum((Integer)queryParameter.findParameterValue("rowSum"));
	}
	
	public void queryMapListForPageByProcedure(final String procedureName, DataPage<Map<String, String>> dataPage)
	{
		Integer beginIndex = (dataPage.getPageNum() - 1) * dataPage.getPageSize();
		Integer endIndex = beginIndex + dataPage.getPageSize();
		Integer rowSum = 0;
		QueryParameter queryParameter = dataPage.getQueryParameter();
		if(queryParameter == null)
		{
			queryParameter = new QueryParameter();
			dataPage.setQueryParameter(queryParameter);
		}
		queryParameter.addParameter("beginIndex", beginIndex);
		queryParameter.addParameter("endIndex", endIndex);
		queryParameter.addOutParameter("rowSum",rowSum);
		queryParameter.removeParameter("pageSize");
		queryParameter.removeParameter("pageNum");
		
		List<Map<String, String>> list = queryMapListByProcedure(procedureName,queryParameter);
		dataPage.setDataList(list);
		dataPage.setRowSum((Integer)queryParameter.findParameterValue("rowSum"));
	}

	
	public List<T> queryListByProcedure(final String procedureName, QueryParameter queryParameter)
	{
		return this.getProcedure().queryList(procedureName,queryParameter);
	}
	
	public void executeProcedure(final String procedureName,  QueryParameter queryParameter)
	{
		this.getProcedure().execute(procedureName,queryParameter);
	}
	
	protected List<T> _queryForPage(DataPage<T> dataPage) 
	{
		dataPage.setRowSum((int)_queryRowCount(dataPage.getHql(),dataPage.getQueryParameter()));
		dataPage.setDataList(this._queryForPage(dataPage.getHql(), dataPage.getQueryParameter(), dataPage.getPageNum(), dataPage.getPageSize()));
		return dataPage.getDataList();
	}

	@Override
	public void save(final List<T> entity) 
	{
		try 
		{
			
			int i=0;
			for(T t:entity)
			{
				this.updateTimestamp(t);
				this.hibernateTemplate.save(t);
				if(i%10==0)
				{
					this.hibernateTemplate.flush();
					this.hibernateTemplate.clear();
					i=0;
					continue;
				}
				i++;
			}
		} catch (Exception e)
		{
		}
		
	}
	/**
	 * add by wzw
	 * 执行hql语句,可执行"delete "语句
	 * @param hql
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void executeSqlToUpdate(final String hql)
	{
		this.getHibernateTemplate().execute(new HibernateCallback()
		{
		       public Object doInHibernate(Session session) throws SQLException,HibernateException
	           {
		         Query query = session.createQuery(hql);
		         query.executeUpdate();
		         return null;
	           }
		});
	}
	
	@Override
	public  T query(QueryParameter queryParameter)
	{
		List<T> entitys = queryList(queryParameter);
		if(entitys != null && entitys.size() > 0)
		{
			return entitys.get(0);
		}
		return null;
	}
	@Override
	public List<T> queryList(QueryParameter queryParameter)
	{
		String hql = createHql(queryParameter);
		return _queryList(hql,queryParameter);
	}
	@Override
	public long queryRowCount(QueryParameter queryParameter)
	{
		String hql = createHql(queryParameter);
		return _queryRowCount(hql,queryParameter);
	}
	@Override
	public List<T> queryForPage(DataPage<T> dataPage)
	{
		QueryParameter queryParameter = dataPage.getQueryParameter();
		if(dataPage.getHql()== null)
		{
			String hql = createHql(queryParameter);
			dataPage.setHql(hql);
		}
		
		if(queryParameter != null)
		{
			if(queryParameter.containParameter("pageSize"))
			{
				dataPage.setPageSize((Integer) queryParameter.findParameterValue("pageSize"));
			}
			if(queryParameter.containParameter("pageNum"))
			{
				dataPage.setPageNum((Integer) queryParameter.findParameterValue("pageNum"));
			}
		}
		this._queryForPage(dataPage);
		return dataPage.getDataList();
	}
	
	public abstract String createHql(QueryParameter queryParameter);
	
	@Override
	public Boolean isChanged(T entity) 
	{
		if(entity == null || this.timestampGetter == null || this.idGetter == null)
		{
			return false;
		}
		Object value = this.timestampGetter.get(entity);
		if(value == null)
		{
			return false;
		}
		String timestamp = (String) value;
		
		T oldEntity = this.queryById((Integer)this.idGetter.get(entity));
		if(oldEntity == null)
		{
			return true;
		}
		else
		{
			String oldTimeStamp = (String)this.timestampGetter.get(oldEntity);
			return !timestamp.equals(oldTimeStamp);
		}
	};

	@Override
	public void updateTimestamp(T entity)
	{
		if(this.timestampSetter != null)
		{
			// 更新时间戳
			this.timestampSetter.set(entity, getTimestamp(), null);
		}	
		else
		{
		}
	};
	
}
